module ActiveBackground
  class Manager < Base
    
    attr_accessor :config
    
    def initialize
      @jobs = Hash.new
      @queue = Hash.new
      @running = true
      @lock = Monitor.new
      @threads = Hash.new
      @terminated = nil
      @logger = ACTIVE_BACKGROUND_CONFIG.logger
      @config = nil
      run
    end
        
    def run
      begin
        @run_thread = Thread.new do
          while @running
            @logger.info "Manager: busy... with #{@jobs.length} jobs"
            process
          end
          @run_thread.kill
        end
      rescue Exception => e
        @logger.error("\n******************************************\n" +
                      "Exception: #{e.to_s} - (#{e.class})\n" +
                      "Manager: Failed while running server\n" +
                      "----------------BACKTRACE-----------------\n" +
                                e.backtrace.join("\n") + "\n" +
                      "-------------------END--------------------\n" +
                      "\n******************************************\n" )
        run
      end
    end

    def process
      unless @jobs.empty?
        @jobs.each do |key, job|
          unless @terminated
            @logger.debug("Manager: Processing job(#{key}) #{job.inspect}")
            if job.status == :created
              @threads[job.key] = Thread.new do
                @logger.info 'Manager: process job with key ' + key.to_s
                job.run()
              end
            else
              remove(job.key) if job.terminated?
            end
          end
          @logger.info "Manager: #{@jobs.length} jobs in queue"
        end
      end
      merge!
      @logger.debug("Manager: Waiting #{@config.server(name).sleep} seconds with #{@jobs.size} jobs running")
      GC.start
      sleep @config.server(name).sleep unless @terminated
    end

    def stop
      @running = false
    end
    
    def merge!
      @lock.synchronize do
        @logger.info("Manager: Merging jobs (#{@jobs.size}) with queue (#{@queue.size}) resulting in #{@jobs.size + @queue.size} running jobs")
        @jobs.merge!(@queue)
        @queue=Hash.new
      end
    end

    def terminate
      unless @terminated and !@running
        @lock.synchronize do
          @terminated = true
          @running = false
        end
      end
      
    end

    def terminated?
      @lock.synchronize do
        @terminated
      end
    end

    def alive?
      true
    end
    
    def name(name=nil)
      if name
        @name=name
      else
        @name
      end
    end
    alias :name= :name

    def wait
      @run_thread.join
    end
    
    def send_command_to_job(key, method, *args)
      @logger.info("Manager: Sending command to job (#{key})")
      merge!
      begin
        @lock.synchronize do
          unless args.empty?
            return @jobs[key].send(method, args) if @jobs[key]
          else
            return @jobs[key].send(method) if @jobs[key]
          end
        end
      rescue Exception => e
        @logger.error("\n******************************************\n" +
                      "Exception: #{e.to_s} - (#{e.class})\n" +
                      "Manager: Cannot send command to job\n" +
                      "----------------BACKTRACE-----------------\n" +
                                e.backtrace.join("\n") + "\n" +
                      "-------------------END--------------------\n" +
                      "\n******************************************\n" )
      end
    end

    def find(job, options={}, *args)
      merge!
      @lock.synchronize do
        if job==:all
          yield @jobs if block_given?
          return @jobs
        elsif @jobs[job]
          return @jobs[job]
        elsif @queue[job]
          return @queue[job]
        end
      end
    end
    
    def count(job, options={}, *args)
      find(job, options, args) {|jobs| return jobs.size}
    end

    def add(job)
      key=job.key
      @logger.info("Manager: adding job to queue")
      @logger.info("Manager: Job - #{job.inspect}")
      merge!
      begin
        @lock.synchronize do
          @queue[key] = job
          @threads[key] = nil
          @logger.debug("Manager: Adding job to queue #{@queue.size} with #{@jobs.size} already running")
        end
      rescue Exception => e
        @logger.error("\n******************************************\n" +
                      "Exception: #{e.to_s} - (#{e.class})\n" +
                      "Manager: Faile while adding job\n" +
                      "----------------BACKTRACE-----------------\n" +
                                e.backtrace.join("\n") + "\n" +
                      "-------------------END--------------------\n" +
                      "\n******************************************\n" )
      end
    end
    
    private
      def remove(key)
        @logger.info("Manager: removing job")
        merge!
        @lock.synchronize do
          @jobs.delete(key)
          @threads[key].kill if @threads[key]
          @threads.delete(key)
        end
      end
  end
end